package fun.flyee.android.pengshan.services;

import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.text.TextUtils;

import com.alibaba.fastjson.JSONObject;
import com.amap.api.maps.model.LatLng;
import com.amap.api.track.AMapTrackClient;
import com.amap.api.track.ErrorCode;
import com.amap.api.track.OnTrackLifecycleListener;
import com.amap.api.track.TrackParam;
import com.amap.api.track.query.entity.CorrectMode;
import com.amap.api.track.query.entity.DenoiseMode;
import com.amap.api.track.query.entity.DriveMode;
import com.amap.api.track.query.entity.LatestPoint;
import com.amap.api.track.query.entity.LocationMode;
import com.amap.api.track.query.entity.MapMatchMode;
import com.amap.api.track.query.entity.Point;
import com.amap.api.track.query.entity.Track;
import com.amap.api.track.query.model.AddTerminalRequest;
import com.amap.api.track.query.model.AddTerminalResponse;
import com.amap.api.track.query.model.AddTrackRequest;
import com.amap.api.track.query.model.AddTrackResponse;
import com.amap.api.track.query.model.LatestPointRequest;
import com.amap.api.track.query.model.LatestPointResponse;
import com.amap.api.track.query.model.QueryTerminalRequest;
import com.amap.api.track.query.model.QueryTerminalResponse;
import com.amap.api.track.query.model.QueryTrackRequest;
import com.amap.api.track.query.model.QueryTrackResponse;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import fun.flyee.android.pengshan.Constant;
import fun.flyee.android.pengshan.api.ApiClient;
import fun.flyee.android.pengshan.models.Terminal;
import fun.flyee.android.pengshan.utils.DateUtil;
import fun.flyee.android.pengshan.utils.Logger;
import fun.flyee.android.pengshan.utils.Systems;
import fun.flyee.android.pengshan.utils.Util;

public class EagleTracker implements Tracker, OnTrackLifecycleListener {

    private Context mContext;
    private String terminalName;
    private long serviceId;
    private long terminalId;
    private long trackId;
    private boolean isServiceRunning;
    private boolean isGatherRunning;
    private AMapTrackClient aMapTrackClient;
    private Handler mHandler;

    private Point lastPosition;

    public EagleTracker(Context context, String terminalName) {
        this.mContext = context;
        mHandler = new Handler(Looper.getMainLooper());
        this.terminalName = terminalName;
        aMapTrackClient = new AMapTrackClient(mContext);
        aMapTrackClient.setLocationMode(LocationMode.HIGHT_ACCURACY);
//        aMapTrackClient.setLocationMode(LocationMode.DEVICE_SENSORS);
        aMapTrackClient.setInterval(5, 60);
    }

    @Override
    public void start() {
        mHandler.post(() -> {
            JSONObject json = new JSONObject();
            json.put("userId", terminalName);
            ApiClient.get("/back-stage/trajectory/getTerminal", json, resp -> {
                if (resp.code == 200) {
                    Terminal terminal = JSONObject.parseObject(resp.data.toString(), Terminal.class);
                    serviceId = terminal.serviceId;
                    terminalId = terminal.terminalId;
                    if (DateUtil.isToday(terminal.date)) {
                        trackId = terminal.trackId;
                    }
                    if (serviceId > 0) {
                        mHandler.post(() -> createTerminal());
                    }
                } else {
                    ApiClient.log(terminalName, "1." + resp.code, resp.msg);
                }
            });
        });
    }

    public void createTerminal() {
        if (terminalId > 0) {
            addTrack();
            return;
        }
        if (aMapTrackClient == null) return;
        aMapTrackClient.queryTerminal(new QueryTerminalRequest(serviceId, terminalName), new TrackListener() {
            @Override
            public void onQueryTerminalCallback(QueryTerminalResponse queryTerminalResponse) {
                if (queryTerminalResponse.isSuccess()) {
                    if (queryTerminalResponse.isTerminalExist()) {
                        // 当前终端已经创建过，直接使用查询到的terminal id
                        terminalId = queryTerminalResponse.getTid();
                        addTrack();
                    } else {
                        // 当前终端是新终端，还未创建过，创建该终端并使用新生成的terminal id
                        if (aMapTrackClient == null) return;
                        aMapTrackClient.addTerminal(new AddTerminalRequest(terminalName, serviceId), new TrackListener() {
                            @Override
                            public void onCreateTerminalCallback(AddTerminalResponse addTerminalResponse) {
                                if (addTerminalResponse.isSuccess()) {
                                    terminalId = addTerminalResponse.getTid();
                                    addTrack();
                                } else {
                                    Logger.e("tracker network error: " + addTerminalResponse.getErrorMsg());
                                    ApiClient.log(terminalName, "2." + addTerminalResponse.getErrorCode(), addTerminalResponse.getErrorMsg());
                                }
                            }
                        });
                    }
                } else {
                    Logger.e("tracker network error: " + queryTerminalResponse.getErrorMsg());
                    ApiClient.log(terminalName, "3." + queryTerminalResponse.getErrorCode(), queryTerminalResponse.getErrorMsg());
                }
            }
        });
    }

    private void addTrack() {
        if (trackId > 0) {
            TrackParam trackParam = new TrackParam(serviceId, terminalId);
            if (aMapTrackClient == null) return;
            aMapTrackClient.startTrack(trackParam, EagleTracker.this);
            return;
        }
        if (aMapTrackClient == null) return;
        aMapTrackClient.addTrack(new AddTrackRequest(serviceId, terminalId), new TrackListener() {
            @Override
            public void onAddTrackCallback(AddTrackResponse addTrackResponse) {
                if (addTrackResponse.isSuccess()) {
                    // trackId需要在启动服务后设置才能生效，因此这里不设置，而是在startGather之前设置了track id
                    trackId = addTrackResponse.getTrid();
                    TrackParam trackParam = new TrackParam(serviceId, terminalId);
                    if (aMapTrackClient == null) return;
                    aMapTrackClient.startTrack(trackParam, EagleTracker.this);
                    saveTerminal();
                } else {
                    Logger.e("tracker network error: " + addTrackResponse.getErrorMsg());
                    ApiClient.log(terminalName, "4." + addTrackResponse.getErrorCode(), addTrackResponse.getErrorMsg());
                }
            }
        });
    }

    private void saveTerminal() {
        JSONObject json = new JSONObject();
        json.put("terminalId", terminalId);
        json.put("trackId", trackId);
        json.put("model", Systems.getDeviceBrand() + "/" + Systems.getSystemModel() + "/Android" + Systems.getSystemVersion());
        ApiClient.post("/back-stage/trajectory/setTerminal", json, resp -> {
            if (resp.code == 200) {
                Logger.i("save terminal success");
            } else {
                Logger.e("save terminal failed: " + resp.code + " " + resp.msg);
                ApiClient.log(terminalName, "5." + resp.code, resp.msg);
            }
        });
    }

    private void startGather() {
        if (aMapTrackClient == null) return;
        aMapTrackClient.setTrackId(trackId);
        aMapTrackClient.startGather(this);
    }

    @Override
    public void fetch(FetchCallback callback) {
        if (!isGatherRunning) return;
        Logger.i("tracegeo", "trackid: " + trackId);
        QueryTrackRequest queryTrackRequest = new QueryTrackRequest(
                serviceId,
                terminalId,
                trackId,     // 轨迹id，不指定，查询所有轨迹，注意分页仅在查询特定轨迹id时生效，查询所有轨迹时无法对轨迹点进行分页
                System.currentTimeMillis() - 3_600_000,
                System.currentTimeMillis(),
                DenoiseMode.NON_DENOSE,      // 不启用去噪
                MapMatchMode.MAPMATCH,   // 绑路
                0,      // 不进行精度过滤
                DriveMode.DRIVING,  // 当前仅支持驾车模式
                0,     // 距离补偿
                5000,   // 距离补偿，只有超过5km的点才启用距离补偿
                1,  // 结果应该包含轨迹点信息
                1,  // 返回第1页数据，但由于未指定轨迹，分页将失效
                500    // 一页不超过100条
        );
        if (aMapTrackClient == null) return;
        aMapTrackClient.queryTerminalTrack(queryTrackRequest, new TrackListener() {
            @Override
            public void onQueryTrackCallback(QueryTrackResponse queryTrackResponse) {
                if (queryTrackResponse.isSuccess()) {
                    List<Track> tracks = queryTrackResponse.getTracks();
                    if (tracks != null && !tracks.isEmpty()) {
                        Track track = tracks.get(0);
                        List<Point> points = track.getPoints();
                        if (points != null && points.size() > 0 && callback != null) {
                            List<LatLng> lls = new ArrayList<>();
                            for (Point p : points) {
                                lls.add(new LatLng(p.getLat(), p.getLng()));
                            }
                            callback.fetch(lls);
                            resetTrack();
                        }
                    } else {
                        Logger.e("tracegeo|未获取到轨迹");
                    }
                } else {
                    Logger.e("tracegeo|查询历史轨迹失败，" + queryTrackResponse.getErrorMsg());
                }
            }
        });
    }

    @Override
    public void getLast(FetchCallback callback) {
        LatestPointRequest req = new LatestPointRequest(serviceId, terminalId);
        if (aMapTrackClient == null) return;
        aMapTrackClient.queryLatestPoint(req, new TrackListener() {
            @Override
            public void onLatestPointCallback(LatestPointResponse latestPointResponse) {
                if (latestPointResponse.isSuccess()) {
                    Point point = latestPointResponse.getLatestPoint().getPoint();
                    if (point == null || point.getLat() == 0d || point.getLng() == 0d) return;
                    if (lastPosition != null) {
                        if (lastPosition.getLat() != point.getLat() || lastPosition.getLng() != point.getLng()) {
                            lastPosition = point;
                            callback.fetch(Arrays.asList(new LatLng(lastPosition.getLat(), lastPosition.getLng())));
                        }
                    } else {
                        lastPosition = point;
                        callback.fetch(Arrays.asList(new LatLng(lastPosition.getLat(), lastPosition.getLng())));
                    }
                }
            }
        });
    }

    private void resetTrack() {
        stopTrack(aMapTrackClient);
        addTrack();
        ApiClient.log(terminalName, "6.reset", "");
    }

    private void stopTrack(AMapTrackClient cli) {
        if (isServiceRunning) {
            mHandler.post(() -> {
                cli.stopGather(this);
                cli.stopTrack(new TrackParam(serviceId, terminalId), new TrackLifecycleListener());
            });
        }
    }

    @Override
    public void destroy() {
        stopTrack(aMapTrackClient);
        isGatherRunning = false;
        isServiceRunning = false;
        mContext = null;
        aMapTrackClient = null;
    }

    @Override
    public void onBindServiceCallback(int i, String s) {

    }

    @Override
    public void onStartGatherCallback(int status, String msg) {
        if (status == ErrorCode.TrackListen.START_GATHER_SUCEE) {
            isGatherRunning = true;
        } else if (status == ErrorCode.TrackListen.START_GATHER_ALREADY_STARTED) {
            isGatherRunning = true;
        } else {
            Logger.e("gps tracker gather error: " + status + ", msg: " + msg);
        }
    }

    @Override
    public void onStartTrackCallback(int status, String msg) {
        if (status == ErrorCode.TrackListen.START_TRACK_ALREADY_STARTED
                || status == ErrorCode.TrackListen.START_TRACK_SUCEE
                || status == ErrorCode.TrackListen.START_TRACK_SUCEE_NO_NETWORK) {
            // 成功启动
            isServiceRunning = true;
            startGather();
        } else {
            Logger.i("gps tracker error: " + status + " | " + msg);
            ApiClient.log(terminalName, "track-error:" + status, Util.substr(msg));
        }
    }

    @Override
    public void onStopGatherCallback(int status, String msg) {
        if (status == ErrorCode.TrackListen.STOP_GATHER_SUCCE) {
            isGatherRunning = false;
        }
    }

    @Override
    public void onStopTrackCallback(int status, String msg) {
        if (status == ErrorCode.TrackListen.STOP_TRACK_SUCCE) {
            // 成功停止
            isServiceRunning = false;
            isGatherRunning = false;
        }
    }

}
